package org.glassunit.core.deploy;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

/**
 * Base for all modules.
 * @author Frederic Müller
 */
public class Module {

    private final Properties properties = new Properties();
    private final String name;
    private final Set<Module> subModules = new HashSet<Module>();

    /**
     * Create a new module instance with the given name.
     * @param name name
     */
    public Module(final String name) {
        if (name == null) {
            throw new NullPointerException("Missing name parameter");
        }
        this.name = name;
    }

    /**
     * Gets the module's name.
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * Gets the module's properties.
     * @return properties
     */
    public Properties getProperties() {
        return properties;
    }

    /**
     * Gets the module's sub modules.
     * @return list of sub modules.
     */
    public Set<Module> getSubModules() {
        return subModules;
    }

    /**
     * Gets a list of all files contained in this module and it's sub modules.
     * @return list of files
     */
    public Set<String> getFiles() {
        final Set<String> files = new HashSet<String>();
        for (Module module : subModules) {
            for (String file : module.getFiles()) {
                files.add(module.getName() + "/" + file);
            }
        }

        return files;
    }

    /**
     * Gets a list of all directories contained in this module and it's sub modules.
     * @return directories
     */
    public Set<String> getDirectories() {
        final Set<String> files = new HashSet<String>();
        for (Module module : subModules) {
            files.add(module.getName());
            for (String directory : module.getDirectories()) {
                files.add(module.getName() + "/" + directory);
            }
        }

        return files;
    }

    /**
     * Gets the content of a specific file.
     * If a file can't be found in this module the request is directed to the sub modules.
     * @param filename filaname
     * @return file content
     * @throws IOException thrown if the file could not be retrieved
     */
    public InputStream getFile(final String filename) throws IOException {
        for (Module module : subModules) {
            if (filename.startsWith(module.getName())) {
                final InputStream result = module.getFile(filename.substring(module.getName().length() + 1));
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

    /**
     * Gets the size of a specific file.
     * If a file can't be found in this module the request is directed to the sub modules.
     * @param filename filename
     * @return file size
     */
    public Long getFileSize(final String filename) {
        for (Module module : subModules) {
            if (filename.startsWith(module.getName())) {
                final Long result = module.getFileSize(filename.substring(module.getName().length() + 1));
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

    /**
     * Checks if a specific file exists.
     * If a file can't be found in this module the request is directed to the sub modules.
     * @param filename filename
     * @return existence of the file
     */
    public boolean exists(final String filename) {
        for (Module module : subModules) {
            if (filename.startsWith(module.getName()) && module.exists(filename.substring(module.getName().length() + 1))) {
                return true;
            }
        }
        return false;
    }

    /**
     * Closes the module.
     * @throws IOException thrown if the module could not be closed
     */
    public void close() throws IOException {
    }

    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder("Module: " + getName());
        for (String file : getFiles()) {
            builder.append("\n\t" + file);
        }
        return builder.toString();
    }
}
